package com.junpuxin.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.junpuxin.constant.MC;
import com.junpuxin.dto.*;
import com.junpuxin.entity.Menu;
import com.junpuxin.entity.RoleMenu;
import com.junpuxin.excel.MenuExcel;
import com.junpuxin.exception.IllegalParamException;
import com.junpuxin.exception.ServerErrorException;
import com.junpuxin.exception.VersionException;
import com.junpuxin.mapper.MenuMapper;
import com.junpuxin.service.MenuService;
import com.junpuxin.vo.MenuVO;
import jakarta.annotation.Resource;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author junpuxin
 */
@Service
@CacheConfig(cacheNames = "menu")
public class MenuServiceImpl implements MenuService {

    @Resource
    private MenuMapper menuMapper;

    @CacheEvict(allEntries = true)
    @Override
    public int insert(MenuInsertDTO dto) {
        String info = dto.getInfo();
        // 拷贝属性
        Menu menu = BeanUtil.copyProperties(dto, Menu.class);

        // 处理父权限
        if (ObjectUtil.isNull(menu.getPid()) || menu.getPid() == 0) {
            menu.setPid(0L);
            menu.setUrl("/");
        }

        // 处理权限路径: 必须以"/"开头
        String url = menu.getUrl();
        if (ObjectUtil.isNotNull(url) && !url.startsWith("/")) {
            menu.setUrl("/" + url);
        }

        // 设置默认值
        menu.setVersion(0L);
        menu.setDeleted(0);
        menu.setInfo(StrUtil.isBlank(info) ? "暂无描述" : info);
        menu.setCreated(LocalDateTime.now());
        menu.setUpdated(LocalDateTime.now());
        // DB添加
        int result = menuMapper.insert(menu);
        if (result < 0) {
            throw new ServerErrorException("DB添加失败");
        }
        return result;
    }

    @Cacheable(key = "#p0", condition = "#p0 != null", unless = "#result == null")
    @Override
    public Menu select(Long id) {
        // DB查询
        Menu result = menuMapper.select(id);
        if(ObjectUtil.isNull(result)) {
            throw new ServerErrorException("记录不存在");
        }
        return result;
    }

    @Cacheable(key = "#root.methodName", unless = "#result == null")
    @Override
    public List<MenuVO> list() {
        return menuMapper.list(new MenuPageDTO())
                .stream()
                .map(menu -> {
                    MenuVO menuVO = BeanUtil.copyProperties(menu, MenuVO.class);
                    if (ObjectUtil.isNotNull(menu.getParent())) {
                        menuVO.setParentTitle(menu.getParent().getTitle());
                    } else {
                        menuVO.setParentTitle("无");
                    }
                    return menuVO;
                })
                .collect(Collectors.toList());
    }

    @Cacheable(key = "#root.methodName + ':' + #p0.toString()",
                condition = "#p0 != null",
                unless = "#result == null")
    @Override
    public PageInfo<Menu> page(MenuPageDTO dto) {
        PageHelper.startPage(dto.getPageNum(), dto.getPageSize());
        return new PageInfo<>(menuMapper.list(dto));
    }

    @CacheEvict(allEntries = true)
    @Transactional
    @Retryable(retryFor = VersionException.class)
    @Override
    public int update(MenuUpdateDTO dto) {
        Long id = dto.getId();
        if(ObjectUtil.isNull(id)) {
            throw new IllegalParamException("ID为空");
        }
        Menu menu = menuMapper.select(id);
        if(ObjectUtil.isNull(menu)) {
            throw new ServerErrorException("记录不存在");
        }
        BeanUtil.copyProperties(dto, menu);
        // 设置默认值
        menu.setUpdated(LocalDateTime.now());
        // DB修改
        int result = menuMapper.update(menu);
        if (result <= 0) {
            throw new VersionException("DB修改失败");
        }
        return result;
    }

    @CacheEvict(allEntries = true)
    @Override
    public int delete(Long id) {
        int result = menuMapper.delete(id);
        if (result <= 0) {
            throw new ServerErrorException("DB删除失败");
        }
        return result;
    }

    @CacheEvict(allEntries = true)
    @Override
    public int deleteBatch(List<Long> ids) {
        int result = menuMapper.deleteBatch(ids);
        if (result <= 0) {
            throw new ServerErrorException("DB批量删除失败");
        }
        return result;
    }

    @Override
    public List<MenuExcel> getExcelData() {
        return menuMapper.list(new MenuPageDTO())
                .stream()
                .map(menu -> {
                    MenuExcel menuExcel = BeanUtil.copyProperties(menu, MenuExcel.class);
                    if (menu.getPid().equals(MC.Menu.ROOT_ID)) {
                        menuExcel.setParentTitle("无");
                    } else {
                        menuExcel.setParentTitle(menu.getParent().getTitle());
                    }
                    return menuExcel;
                })
                .collect(Collectors.toList());
    }

    @Cacheable(key = "#p0", condition = "#p0 != null", unless = "#result == null")
    @Override
    public List<Menu> listByRoleId(Long roleId) {
        List<Menu> menus = menuMapper.listByRoleId(roleId);
        if (ObjectUtil.isNull(menus)) {
            throw new ServerErrorException("记录不存在");
        }
        return menus;
    }

    @CacheEvict(allEntries = true)
    @Transactional
    @Retryable(retryFor = VersionException.class)
    @Override
    public int updateByRoleId(Long roleId, List<Long> menuIds) {
        // 删除该角色的全部中间表记录
        int deleteResult = menuMapper.deleteRoleMenuByRoleId(roleId);
        // 创建该菜单的新菜单列表
        List<RoleMenu> roleMenus = new ArrayList<>();
        for (Long menuId : menuIds) {
            RoleMenu roleMenu = new RoleMenu();
            roleMenu.setFkRoleId(roleId);
            roleMenu.setFkMenuId(menuId);
            roleMenu.setVersion(0L);
            roleMenu.setDeleted(0);
            roleMenu.setCreated(LocalDateTime.now());
            roleMenu.setUpdated(LocalDateTime.now());
            roleMenus.add(roleMenu);
        }
        // 批量添加该角色的菜单记录(中间表记录)
        int insertResult = menuMapper.insertRoleMenuBatch(roleMenus);
        return deleteResult + insertResult;
    }

    @Cacheable(key = "#p0", condition = "#p0 != null", unless = "#result == null")
    @Override
    public List<Menu> listByEmpId(Long empId) {
        List<Menu> menus = menuMapper.listByEmpId(empId);
        if (ObjectUtil.isNull(menus)) {
            throw new ServerErrorException("记录不存在");
        }
        return menus;
    }
}
